
`include "common_header.verilog"

//  *************************************************************************
//  File : tx_ff64v2id
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized reproduction or use is expressly prohibited.
//  Copyright (c) 2014 MorethanIP
//  Muenchner Strasse 199, 85757 Karlsfeld, Germany
//  info@morethanip.com
//  http://www.morethanip.com
//  *************************************************************************
//  Designed by : Daniel Koehler, Thomas Schrobenhauser
//  info@morethanip.com
//  *************************************************************************
//  Description : Transmit FIFO Structure with timestamp/frame-ID context
//  Version     : $Id: tx_ff64v2id.v,v 1.11 2016/12/21 11:20:34 dk Exp $
//  *************************************************************************

module tx_ff64v2id (
   reset_txclk,
   reset_ff_tx_clk,
   tx_sw_reset,
   tx_sw_reset_done,
   ff_tx_clk,
   ff_tx_data,
   ff_tx_sop,
   ff_tx_eop,
   ff_tx_mod,
   ff_tx_err,
   ff_tx_crc,
   ff_tx_wren,
   ff_tx_rdy,
   ff_tx_septy,
`ifdef MTIP_PARITY_PROT
   par_odd,
   tx_par_check_en,
   ff_tx_data_par,
   ff_tx_ctrl_par,
   ff_tx_data_par_err,
   ff_tx_ctrl_par_err,
`endif
   ff_tx_id,
   ff_tx_ts_frm,
   ff_tx_ovr_err,
`ifdef USER_PREAMBLE_ENA
   ff_tx_preamble,
`endif
   section,
   sect_e,
   txclk,
   txclk_ena,
   tx_stat_empty,
   tx_stat_mod,
   tx_stat_err,
   tx_stat_crc,
   tx_stat_id,
   tx_stat_ts_frm,
   tx_data_int,
   tx_data_err_int,
   tx_sav_int,
   tx_empty,
   tx_sop_int,
   tx_eop_int,
`ifdef USER_PREAMBLE_ENA
   tx_preamble_int,
   tx_rden_int_sop,
`endif
   tx_rden_int
`ifdef MTIPMAC_1SIF
   ,
   os_dff_wren
`endif
`ifdef FFMEM_EXTERNAL
   ,
   txd_raddr,
   txd_rden,
   txd_rdata,
   txd_recc,
   txd_wren,
   txd_waddr,
   txd_wdata,
   txs_raddr,
   txs_rden,
   txs_rdata,
   txs_wren,
   txs_waddr,
   txs_wdata
   `ifdef USER_PREAMBLE_ENA
   ,
   txp_raddr,
   txp_rden,
   txp_rdata,
   txp_wren,
   txp_waddr,
   txp_wdata
   `endif
`endif
   );

parameter FF_WIDTH      = 64;   // Data FIFO width (32, 64)
parameter DEPTH         = 256;  // Data FIFO depth (max 512)
parameter ADDR_WIDTH    = 8;    // Data FIFO address bits
parameter ST_DEPTH      = 256;  // Status FIFO depth
parameter ST_ADDR_WIDTH = 8;    // Status FIFO address bits
parameter MOD_WIDTH     = 3;    // Modulo bits needed to support FF_WIDTH
parameter AF_THRESHOLD  = 7;    // Data FIFO almost full (minimum 7)
parameter AE_THRESHOLD  = 3;    // Data FIFO almost empty (minimum 3)
parameter TSID_WIDTH    = 4;    // Width for frame-ID

`ifdef MTIP_PARITY_PROT
localparam SFF_LO_WIDTH = 1 + 13 + 1 + 1 + 1;  //  ctrl par+len(13)+tsfrm(1)+crc(1)+err(1)
localparam SFF_WIDTH    = 1 + 13 + 1 + 1 + 1 + TSID_WIDTH + MOD_WIDTH;  //  ctrl par+len(13)+tsfrm(1)+crc(1)+err(1)+id(4)+mod(3)
`else
localparam SFF_LO_WIDTH = 13 + 1 + 1 + 1;  //  len(13)+tsfrm(1)+crc(1)+err(1)
localparam SFF_WIDTH    = 13 + 1 + 1 + 1 + TSID_WIDTH + MOD_WIDTH;  //  len(13)+tsfrm(1)+crc(1)+err(1)+id(4)+mod(3)
`endif

input   reset_txclk;                    //  Async active high reset for txclk domain
input   reset_ff_tx_clk;                //  Async active high reset for ff_tx_clk domain
input   tx_sw_reset;                    //  FIFO sync reset (ff_tx_clk)
output  tx_sw_reset_done;               //  FIFO reset completed, pulse (ff_tx_clk)
input   ff_tx_clk;                      //  Transmit Application Clock
input   [63:0] ff_tx_data;              //  Transmit Data
input   ff_tx_sop;                      //  Start of Packet
input   ff_tx_eop;                      //  End of Packet
input   [MOD_WIDTH-1:0] ff_tx_mod;      //  Last Word Modulo
input   ff_tx_err;                      //  Errored Packet
input   ff_tx_crc;                      //  Append CRC on transmit
input   ff_tx_wren;                     //  Write Enable
output  ff_tx_rdy;                      //  FIFO Ready
output  ff_tx_septy;                    //  Section Empty in Transmit FIFO
`ifdef MTIP_PARITY_PROT
input   par_odd;                        //  If 1, odd parity used, otherwise even parity
input   tx_par_check_en;                //  If 0, no Tx parity check
input   [7:0] ff_tx_data_par;           //  Data Parity In
input   ff_tx_ctrl_par;                 //  Control Parity In
output  ff_tx_data_par_err;             //  Data Parity Error
output  ff_tx_ctrl_par_err;             //  Control Parity Error
`endif
input   [TSID_WIDTH-1:0] ff_tx_id;      //  TX frame identifier
input   ff_tx_ts_frm;                   //  Store timestamp command
output  ff_tx_ovr_err;                  //  FIFO overflow/truncation occured
`ifdef USER_PREAMBLE_ENA
input   [55:0] ff_tx_preamble;          //  Transmit preamble (0xd5555555555555)
`endif
input   [ADDR_WIDTH-1:0] section;       //  Section Threshold Used to Genererate SAV
input   [ADDR_WIDTH-1:0] sect_e;        //  Section Threshold Used to Genererate SEPTY
input   txclk;                          //  Read Clock
input   txclk_ena;                      //  Read Clock Enable
output  tx_stat_empty;                  //  Status FIFO Empty
output  [MOD_WIDTH-1:0] tx_stat_mod;    //  Status with Last Word
output  tx_stat_err;                    //  Status with Last Word
output  tx_stat_crc;                    //  Status with Last Word
output  [TSID_WIDTH-1:0] tx_stat_id;    //  Frame identifier
output  tx_stat_ts_frm;                 //  Store timestamp command
output  [63:0] tx_data_int;             //  FIFO Transmit Data
output  tx_data_err_int;                //  Data from FIFO memory is erroneous (ECC)
output  tx_sav_int;                     //  Data Section Available in FIFO
output  tx_empty;                       //  Transmit Data FIFO Almost Empty
output  tx_sop_int;                     //  Start of Packet
output  tx_eop_int;                     //  End of Packet
`ifdef USER_PREAMBLE_ENA
output  [55:0] tx_preamble_int;         //  Transmit preamble (0xd5555555555555)
input   tx_rden_int_sop;                //  Transmit reads first word from FIFO, unused
`endif
input   tx_rden_int;                    //  Data FIFO transmit Read Enable
`ifdef MTIPMAC_1SIF
output  os_dff_wren;                    //  Write Enable (with input protection)
`endif

`ifdef FFMEM_EXTERNAL   // External Memories

  output [ADDR_WIDTH-1:0] txd_raddr;    //  TX read address (tx_clk)
  output                  txd_rden;     //  TX read enable (tx_clk)
  input  [FF_WIDTH+2-1:0] txd_rdata;    //  TX read data (tx_clk)
  input                   txd_recc;     //  TX read data ecc error (tx_clk)
  output                  txd_wren;     //  TX write (ff_tx_clk)
  output [ADDR_WIDTH-1:0] txd_waddr;    //  TX write addres (ff_tx_clk)
  output [FF_WIDTH+2-1:0] txd_wdata;    //  TX write data (ff_tx_clk)

  wire  [ADDR_WIDTH-1:0]  txd_raddr;
  wire                    txd_rden;
  wire                    txd_wren;
  wire  [ADDR_WIDTH-1:0]  txd_waddr;
  wire  [FF_WIDTH+2-1:0]  txd_wdata;

  output [ST_ADDR_WIDTH-1:0] txs_raddr; //  TX status read address (tx_clk)
  output                  txs_rden;     //  TX status read enable (tx_clk)
  input  [SFF_WIDTH-1:0]  txs_rdata;    //  TX status read data (tx_clk)
  output                  txs_wren;     //  TX status write (ff_tx_clk)
  output [ST_ADDR_WIDTH-1:0] txs_waddr; //  TX status write addres (ff_tx_clk)
  output [SFF_WIDTH-1:0]  txs_wdata;    //  TX status write data (ff_tx_clk)

  wire  [ST_ADDR_WIDTH-1:0] txs_raddr;
  wire                    txs_rden;
  wire                    txs_wren;
  wire  [ST_ADDR_WIDTH-1:0]  txs_waddr;
  wire  [SFF_WIDTH-1:0]   txs_wdata;

  `ifdef USER_PREAMBLE_ENA
  output [ST_ADDR_WIDTH-1:0] txp_raddr; //  TX preamble read address (tx_clk)
  output                  txp_rden;     //  TX preamble read enable (tx_clk)
  input  [55:0]           txp_rdata;    //  TX preamble read data (tx_clk)
  output                  txp_wren;     //  TX preamble write (ff_tx_clk)
  output [ST_ADDR_WIDTH-1:0] txp_waddr; //  TX preamble write addres (ff_tx_clk)
  output [55:0]           txp_wdata;    //  TX preamble write data (ff_tx_clk)

  wire  [ST_ADDR_WIDTH-1:0] txp_raddr;
  wire                    txp_rden;
  wire                    txp_wren;
  wire  [ST_ADDR_WIDTH-1:0]  txp_waddr;
  wire  [55:0]            txp_wdata;
  `endif
  
`endif

wire    tx_sw_reset_done;
reg     ff_tx_rdy;
wire    ff_tx_septy;
`ifdef MTIP_PARITY_PROT
reg     ff_tx_data_par_err;
reg     ff_tx_ctrl_par_err;
`endif
wire    ff_tx_ovr_err;
wire    tx_stat_empty;
wire    [MOD_WIDTH-1:0] tx_stat_mod;
wire    tx_stat_err;
reg     tx_stat_crc;
reg     [TSID_WIDTH-1:0] tx_stat_id;
reg     tx_stat_ts_frm;
wire    [63:0] tx_data_int;
wire    tx_data_err_int;
wire    tx_sav_int;
wire    tx_empty;
wire    tx_sop_int;
wire    tx_eop_int;
`ifdef MTIPMAC_1SIF
wire    os_dff_wren;
`endif

//  SOP status FIFO (preamble)
//  --------------------------
`ifdef USER_PREAMBLE_ENA

wire    [55:0] tx_preamble_int; //  Transmit preamble to MAC (0xd5555555555555)
wire    sop_wren;
wire    sop_wren_d;
wire    [55:0] ff_tx_preamble_d;
wire    [55:0] sop_dout;
wire    sop_rden;

  `ifdef MTIPSDPM_GEN_READEN
  wire  sop_empty;
  wire  sop_empty_n;
  `endif

`endif

wire    tx_rdy;                 //  FIFO ready to accept data
wire    stat_wren;              //  Status FIFO write enable
wire    stat_err;               //  Error bit to write into status FIFO
wire    [12:0] stat_wcnt;       //  Number of words written for status FIFO
wire    dff_wren;               //  Data FIFO write enable

//  Status FIFO
//  -----------

wire    stat_afull;
wire    stat_empty;
reg     stat_empty_d;
wire    stat_aempty;
wire    [TSID_WIDTH - 1:0]   sff_din_id;
wire    [MOD_WIDTH - 1:0]    sff_din_mod;
wire    [SFF_LO_WIDTH - 1:0] sff_din_lo;
wire    sff_wren;
wire    [SFF_WIDTH - 1:0] sff_din;
wire    [SFF_WIDTH - 1:0] sff_dout;
wire    sff_rden;
wire    [12:0] frm_length;
wire    stat_rden_reg;
wire    [MOD_WIDTH-1:0] mac_stat_mod;   //  Status with Last Word Modulo
reg     [MOD_WIDTH-1:0] mac_stat_mod_r; //  Status with Last Word Modulo registered
wire    mac_stat_err;                   //  Status with Last Word Modulo
reg     mac_stat_err_r;                 //  Status with Last Word Modulo registered
wire    mac_stat_crc;                   //  Status with Last Word Modulo
wire    [TSID_WIDTH-1:0] mac_stat_id;   //  Frame identifier
wire    mac_stat_ts_frm;                //  Store timestamp command

//  Data FIFO
//  ---------
wire    dff_afull;                      //  Data FIFO almost full (write clock domain)
wire    dff_sav;                        //  Data section available (MAC can start reading) (read clock)
reg     dff_sav_d;                      //  Data section available (MAC can start reading) (read clock)
wire    dff_aempty;                     //  Data FIFO almost empty (read clock)
wire    dff_empty;                      //  Data FIFO almost full (write clock domain)
wire    [ADDR_WIDTH - 1:0] af_level;    //  Almost Full Threshold
wire    [ADDR_WIDTH - 1:0] ae_level;    //  Almost Empty Threshold
wire    ff_wren;
wire    [1:0] ff_data_in_se;            //  sop/eop
wire    [FF_WIDTH - 1:0] ff_data_in_dat;
wire    [FF_WIDTH+2-1:0] ff_data_in;
wire    [FF_WIDTH+2-1:0] ff_data_out;
wire    ff_data_sop;
wire    ff_data_eop;
wire    ff_data_out_err;                //  Memory read error
wire    ff_data_out_err_mem;            //  Memory read error
wire    ff_rden;                        //  Pure memory read (no address pointer inc)
reg     mac_rden_d;
reg     tx_empty_d;
wire    ds_afull;                       //  Data FIFO OR Status FIFO almost full (write clock domain)
reg     tx_data_err_int_r;
reg     mac_force_eop;

//  Controls
wire    sw_reset_wclk;                  //  Sync reset on write clock domain
wire    sw_reset_rclk;                  //  Sync reset on read clock domain

`ifdef MTIPSDPM_GEN_READEN
wire    stat_empty_n;
`endif

`ifdef MTIP_PARITY_PROT
wire    par_odd_tx;
wire    par_odd_ff;
wire    tx_par_check_en_tx;
wire    tx_par_check_en_ff;
wire    ff_ctrl_par_nxt;
wire    ff_ctrl_par;
wire    ff_ctrl_par_err_inp;
wire    ff_ctrl_par_err_inp_s;
wire    ff_ctrl_par_err_int;
wire    ff_stat_par_in;
wire    ff_stat_par_out;
wire    ff_stat_par_out_exp;
wire    ff_stat_par_out_err_nxt;
reg     ff_stat_par_out_err;
reg     sff_rden_r;
wire    ff_se_par_in;
wire    ff_se_par_out;
wire    ff_se_par_out_exp;
wire    ff_se_par_out_err_nxt;
reg     ff_se_par_out_err;
wire    [7:0] ff_data_par_out;
wire    [7:0] ff_data_par_out_even;
wire    [7:0] ff_data_par_out_exp;
wire    ff_data_par_out_err;
`endif


//  Sync reset controller in application side (write) clock domain
//  ---------------------
ff_sreset U_RST (
          .reset_lclk(reset_ff_tx_clk),         //  application side clock
          .lclk(ff_tx_clk),
          .lsreset_in(tx_sw_reset),             //  trigger reset
          .lsreset_done(tx_sw_reset_done),      //  cycle complete pulse
          .lsreset_out(sw_reset_wclk),          //  write clock domain sync reset
          .reset_oclk(reset_txclk),             //  read side
          .oclk(txclk),
          .osreset_out(sw_reset_rclk));


`ifdef MTIP_PARITY_PROT

mtip_xsync #(2) U_PARTX (
          .data_in({par_odd, tx_par_check_en}),
          .reset(reset_txclk),
          .clk(txclk),
          .data_s({par_odd_tx, tx_par_check_en_tx}));

mtip_xsync #(2) U_PARFF (
          .data_in({par_odd, tx_par_check_en}),
          .reset(reset_ff_tx_clk),
          .clk(ff_tx_clk),
          .data_s({par_odd_ff, tx_par_check_en_ff}));

`endif


//  -----------------------------
//  FIFO write control&protection
//  -----------------------------

tx_ff_wctl U_WCTL (
          .reset_ff_tx_clk(reset_ff_tx_clk),
          .ff_tx_clk(ff_tx_clk),
          .reset_tx_clk(reset_txclk),
          .tx_clk(txclk),
          .sw_reset_wclk(sw_reset_wclk),
          .tx_sop(ff_tx_sop),
          .tx_eop(ff_tx_eop),
          .tx_err(ff_tx_err),
          .tx_wren(ff_tx_wren),
          .tx_rdy(tx_rdy),
          .tx_ovr_err(ff_tx_ovr_err),
          .dff_afull(ds_afull),   // data OR status
          .dff_sav(dff_sav),
          .stat_empty(stat_empty),
          .stat_wren(stat_wren),
          .stat_err(stat_err),
          .stat_wcnt(stat_wcnt),
          .dff_wren(dff_wren)
          );

assign ds_afull = dff_afull | stat_afull;

always @(posedge reset_ff_tx_clk or posedge ff_tx_clk)
   begin : process_1
   if (reset_ff_tx_clk == 1'b 1)
      begin
      ff_tx_rdy <= 1'b 0;   //  read clock domain
      end
   else
      begin
      //  qualified ready from write protection and status FIFO not full
      ff_tx_rdy <= tx_rdy & ~stat_afull;
      end
   end

`ifdef MTIPMAC_1SIF

assign os_dff_wren = dff_wren;

`endif


//  -----------
//  Status FIFO
//  -----------

`ifdef MTIP_PARITY_PROT

// Control parity input check

assign ff_ctrl_par_nxt = ^{ff_tx_sop, ff_tx_eop, ff_tx_mod, ff_tx_err, ff_tx_crc};

assign ff_ctrl_par = (par_odd_ff == 1'b 0) ? ff_ctrl_par_nxt : ~ff_ctrl_par_nxt;

assign ff_ctrl_par_err_inp = (ff_ctrl_par != ff_tx_ctrl_par & ff_tx_wren == 1'b 1 &
                              tx_par_check_en_ff == 1'b 1) ? 1'b 1 : 1'b 0;

redge_ckxing U_SYNCPARERR (
          .reset(reset_ff_tx_clk),
          .clk(ff_tx_clk),
          .sig(ff_ctrl_par_err_inp),
          .reset_clk_o(reset_txclk),
          .clk_o(txclk),
          .sig_o(ff_ctrl_par_err_inp_s));

always @(posedge reset_txclk or posedge txclk)
   begin
   if (reset_txclk == 1'b 1)
      begin
      ff_tx_ctrl_par_err <= 1'b 0;
      end
   else
      begin
      if (tx_par_check_en_tx == 1'b 1)
         begin
         ff_tx_ctrl_par_err <= ff_ctrl_par_err_int | ff_ctrl_par_err_inp_s;
         end
      else
         begin
         ff_tx_ctrl_par_err <= 1'b 0;
         end
      end
   end

assign ff_stat_par_in = ^({ff_tx_crc, stat_err, ff_tx_mod});

`endif

//  register all input data, including the write strobe
mtip_dffvec #(TSID_WIDTH) U_SFFIDREG (
          .reset(reset_ff_tx_clk),
          .clk(ff_tx_clk),
          .i(ff_tx_id),
          .o(sff_din_id));

mtip_dffvec #(MOD_WIDTH) U_SFFMOREG (
          .reset(reset_ff_tx_clk),
          .clk(ff_tx_clk),
          .i(ff_tx_mod),
          .o(sff_din_mod));

mtip_dffvec #(SFF_LO_WIDTH + 1) U_SFFREG (
          .reset(reset_ff_tx_clk),
          .clk(ff_tx_clk),
        `ifdef MTIP_PARITY_PROT
          .i({stat_wren, ff_stat_par_in, ff_tx_ts_frm, ff_tx_crc, stat_err, stat_wcnt}),
        `else
          .i({stat_wren, ff_tx_ts_frm, ff_tx_crc, stat_err, stat_wcnt}),
        `endif
          .o({sff_wren, sff_din_lo}));

assign sff_din = {sff_din_id, sff_din_mod, sff_din_lo};

`ifdef MTIPSDPM_GEN_READEN

    assign stat_empty_n = ~stat_empty;

`endif

`ifdef FFMEM_EXTERNAL
r_fifo_234e 
`else
r_fifo_234 
`endif
        #(.FF_WIDTH(SFF_WIDTH),
          .ADDR_WIDTH(ST_ADDR_WIDTH),
          .DEPTH(ST_DEPTH),
          .AF_THRESHOLD(3),
          .AE_THRESHOLD(2))

        U_SFF (
          .reset_wclk(reset_ff_tx_clk),
          .reset_rclk(reset_txclk),
          .sw_reset_wclk(sw_reset_wclk),
          .sw_reset_rclk(sw_reset_rclk),
          .wclk(ff_tx_clk),
          .wren(sff_wren),
          .din(sff_din),
          .rclk(txclk),
          .rden(sff_rden),
        `ifdef MTIPSDPM_GEN_READEN
          .mem_rden(stat_empty_n),   // immediately produce output as long as non-empty
        `endif
          .dout(sff_dout),
          .afull(stat_afull),
          .empty(stat_empty),
          .aempty(stat_aempty)
        `ifdef FFMEM_EXTERNAL
          ,
          .dout_err(),
          .m_raddr(txs_raddr),
          .m_rden( txs_rden ),
          .m_rdata(txs_rdata),
          .m_recc( 1'b 0 ),
          .m_wren( txs_wren ),
          .m_waddr(txs_waddr),
          .m_wdata(txs_wdata)
        `endif
          );

//  extract fields from status FIFO data output
`ifdef MTIP_PARITY_PROT
assign mac_stat_id     = sff_dout[17+MOD_WIDTH+TSID_WIDTH-1 : 17+MOD_WIDTH];
assign mac_stat_mod    = sff_dout[17+MOD_WIDTH-1 : 17];
`else
assign mac_stat_id     = sff_dout[16+MOD_WIDTH+TSID_WIDTH-1 : 16+MOD_WIDTH];
assign mac_stat_mod    = sff_dout[16+MOD_WIDTH-1 : 16];
`endif
assign mac_stat_ts_frm = sff_dout[15];
assign mac_stat_crc    = sff_dout[14];
assign mac_stat_err    = sff_dout[13];
assign frm_length      = sff_dout[12:0];   //  words in fifo

//  register status outputs
always @(posedge reset_txclk or posedge txclk)
   begin : process_2
   if (reset_txclk == 1'b 1)
      begin
      mac_stat_mod_r <= {MOD_WIDTH{1'b 0}};
      mac_stat_err_r <= 1'b 0;
      mac_force_eop  <= 1'b 0;
      tx_stat_crc    <= 1'b 0;
      tx_stat_id     <= {TSID_WIDTH{1'b 0}};
      tx_stat_ts_frm <= 1'b 0;
      end
   else
      begin
      if (sw_reset_rclk == 1'b 1)
         begin
         mac_stat_err_r <= 1'b 1;   // abort with error if in middle of frame
         mac_force_eop  <= 1'b 1;   // force eop to MAC until next clock enable
         end
      else if (txclk_ena == 1'b 1)
         begin
         mac_stat_err_r <= mac_stat_err;
         mac_force_eop  <= 1'b 0;
         end

      if (txclk_ena == 1'b 1)
         begin
         mac_stat_mod_r <= mac_stat_mod;
         tx_stat_crc    <= mac_stat_crc;
         tx_stat_id     <= mac_stat_id;
         tx_stat_ts_frm <= mac_stat_ts_frm;
         end
      end
   end

assign tx_stat_mod = mac_stat_mod_r;
assign tx_stat_err = mac_stat_err_r | sw_reset_rclk;   // assert immediately with sw_reset_rclk


`ifdef MTIP_PARITY_PROT

assign ff_stat_par_out = sff_dout[16];

// Status parity check

assign ff_stat_par_out_exp = ^({mac_stat_crc, mac_stat_err, mac_stat_mod});

assign ff_stat_par_out_err_nxt = (ff_stat_par_out_exp != ff_stat_par_out);

always @(posedge reset_txclk or posedge txclk)
   begin
   if (reset_txclk == 1'b 1)
      begin
      sff_rden_r <= 1'b 0;
      ff_stat_par_out_err <= 1'b 0;
      end
   else
      begin
      sff_rden_r <= sff_rden;

      if (sff_rden_r == 1'b 1)
         begin
         ff_stat_par_out_err <= ff_stat_par_out_err_nxt;
         end
      end
   end

assign ff_ctrl_par_err_int = ff_se_par_out_err | ff_stat_par_out_err;

`endif


//  --------------
//  Data FIFO
//  --------------

assign af_level = AF_THRESHOLD;
assign ae_level = AE_THRESHOLD;

`ifdef MTIP_PARITY_PROT

assign ff_se_par_in = ff_tx_sop ^ ff_tx_eop;

`endif

//  register all input data, including the write strobe (which comes from input protection combinatorial)
mtip_dffvec #(3) U_DFFSREG (
          .reset(reset_ff_tx_clk),
          .clk(ff_tx_clk),
          .i({dff_wren, ff_tx_eop, ff_tx_sop}),
          .o({ff_wren, ff_data_in_se}));

mtip_dffvec #(FF_WIDTH) U_DFFDREG (
          .reset(reset_ff_tx_clk),
          .clk(ff_tx_clk),
        `ifdef MTIP_PARITY_PROT
          .i({ff_se_par_in, ff_tx_data_par, ff_tx_data}),
        `else
          .i(ff_tx_data),
        `endif
          .o(ff_data_in_dat));

assign ff_data_in = {ff_data_in_se, ff_data_in_dat};

r_fifo_opt_12456e_rena #(
                .FF_WIDTH(FF_WIDTH+2),   // sop/eop
                .DEPTH(DEPTH),
                .ADDR_WIDTH(ADDR_WIDTH))

        U_DFF (
          .reset_wclk(reset_ff_tx_clk),
          .reset_rclk(reset_txclk),
          .sw_reset_wclk(sw_reset_wclk),
          .sw_reset_rclk(sw_reset_rclk),
          .wclk(ff_tx_clk),
          .wren(ff_wren),
          .din(ff_data_in),
          .rclk(txclk),
          .rclk_ena(txclk_ena),
          .rden(ff_rden),
          .dout(ff_data_out),
          .dout_err(ff_data_out_err_mem),
          .section(section),
          .sect_e(sect_e),
          .af_level(af_level),
          .ae_level(ae_level),
          .sav(dff_sav),
          .septy(ff_tx_septy),
          .afull(dff_afull),
          .aempty(dff_aempty),
          .empty(dff_empty)

        `ifdef FFMEM_EXTERNAL
          ,
          .m_raddr(txd_raddr),
          .m_rden( txd_rden ),
          .m_rdata(txd_rdata),
          .m_recc( txd_recc ),
          .m_wren( txd_wren ),
          .m_waddr(txd_waddr),
          .m_wdata(txd_wdata)
        `endif
          );

assign ff_data_sop = ff_data_out[FF_WIDTH];
assign ff_data_eop = ff_data_out[FF_WIDTH+1];


//  FIFO Read Control
//  -----------------
tx_ff_read_cntlv2 U_FF_RD (
          .reset(reset_txclk),
          .clk(txclk),
          .clk_ena(txclk_ena),
          .sw_reset_rclk(sw_reset_rclk),
          .ff_rden(ff_rden),
          .ff_sav(dff_sav),
          .tx_aempty(dff_aempty),
          .mac_rden(tx_rden_int),
          .stat_rden_reg(stat_rden_reg),
          .stat_empty(stat_empty),
          .stat_aempty(stat_aempty),
          .frm_length(frm_length)
          );

assign sff_rden = stat_rden_reg & txclk_ena;

always @(posedge reset_txclk or posedge txclk)
   begin : process_3
   if (reset_txclk == 1'b 1)
      begin
      tx_data_err_int_r <= 1'b 0;
      mac_rden_d      <= 1'b 0;
      end
   else
      begin
      if (sw_reset_rclk == 1'b 1)
         begin
         mac_rden_d <= 1'b 0;
         end
      else if (txclk_ena == 1'b 1)
         begin
         mac_rden_d <= tx_rden_int;
         end

      // ECC error latched until end of frame
      if (txclk_ena == 1'b 1)
         begin
         if (ff_data_eop == 1'b 1)   // eop
            begin
            tx_data_err_int_r <= 1'b 0;
            end
         else if (ff_data_out_err == 1'b 1)
            begin
            tx_data_err_int_r <= 1'b 1;
            end
         end
      end
   end

assign tx_data_err_int = tx_data_err_int_r | ff_data_out_err;


// Wire Tx interface to MAC
// ------------------------
assign tx_sop_int  = mac_rden_d & ff_data_sop;
assign tx_eop_int  = (mac_rden_d & ff_data_eop) |     // mask after eop processed
                     sw_reset_rclk | mac_force_eop;   // force eop if sw_reset_rclk in middle of frame
assign tx_data_int = ff_data_out[63:0];


`ifdef MTIP_PARITY_PROT

// Data parity check

assign ff_data_par_out = ff_data_out[71:64];

assign ff_data_par_out_even = {(^ff_data_out[63:56]),
                               (^ff_data_out[55:48]),
                               (^ff_data_out[47:40]),
                               (^ff_data_out[39:32]),
                               (^ff_data_out[31:24]),
                               (^ff_data_out[23:16]),
                               (^ff_data_out[15:8]),
                               (^ff_data_out[7:0])};

assign ff_data_par_out_exp = (par_odd_tx == 1'b 0) ? ff_data_par_out_even : ~ff_data_par_out_even;

assign ff_data_par_out_err = (ff_data_par_out_exp != ff_data_par_out & tx_par_check_en_tx == 1'b 1) ? 1'b 1 : 1'b 0;

// Control parity check

assign ff_se_par_out = ff_data_out[72];

assign ff_se_par_out_exp = ^ff_data_out[74:73];

assign ff_se_par_out_err_nxt = (ff_se_par_out_exp != ff_se_par_out);

always @(posedge reset_txclk or posedge txclk)
   begin
   if (reset_txclk == 1'b 1)
      begin
      ff_tx_data_par_err <= 1'b 0;
      ff_se_par_out_err <= 1'b 0;
      end
   else
      begin
      if (sw_reset_rclk == 1'b 1)
         begin
         ff_tx_data_par_err <= 1'b 0;
         ff_se_par_out_err <= 1'b 0;
         end
      else if (txclk_ena == 1'b 1 & mac_rden_d == 1'b 1)
         begin
         ff_tx_data_par_err <= ff_data_par_out_err;
         ff_se_par_out_err <= ff_se_par_out_err_nxt;
         end
      end
   end

assign ff_data_out_err = (ff_data_out_err_mem | ff_data_par_out_err) & mac_rden_d;

`else

assign ff_data_out_err = ff_data_out_err_mem & mac_rden_d;

`endif


// Status Flags generation
// -----------------------
always @(posedge reset_txclk or posedge txclk)
   begin : process_4
   if (reset_txclk == 1'b 1)
      begin
      tx_empty_d <= 1'b 1;
      dff_sav_d <= 1'b 0;
      stat_empty_d <= 1'b 1;
      end
   else
      begin
      if (txclk_ena == 1'b 1)
         begin
         if (dff_aempty == 1'b 1 & stat_empty == 1'b 1)
            begin
            tx_empty_d <= 1'b 1;
            end
         else
            begin
            tx_empty_d <= 1'b 0;
            end
         end

      //  One Clock Latency to Allow Pipeline Pre-Load
      //  --------------------------------------------
      if (sw_reset_rclk == 1'b 1)
         begin
         dff_sav_d    <= 1'b 0;
         stat_empty_d <= 1'b 1;
         end
      else if (txclk_ena == 1'b 1)
         begin
         dff_sav_d    <= dff_sav;
         stat_empty_d <= stat_empty;
         end
      end
   end

//  Assertion one clock delayed (pipeline pre-load), de-assertion immediately
assign tx_sav_int    = dff_sav & dff_sav_d;
assign tx_stat_empty = stat_empty | stat_empty_d;
assign tx_empty      = (dff_aempty & stat_empty) | tx_empty_d;


//  ---------------
//  SOP status FIFO, written with start of frame for preamble
//  ---------------

`ifdef USER_PREAMBLE_ENA

        assign sop_wren = ff_tx_sop & dff_wren;   // respect input protection masking

        //  register all input data, including the write strobe (which comes from input protection combinatorial)
        mtip_dffvec #(56+1) U_PRBREG (
                  .reset(reset_ff_tx_clk),
                  .clk(ff_tx_clk),
                  .i({sop_wren, ff_tx_preamble}),
                  .o({sop_wren_d, ff_tx_preamble_d}));

        `ifdef MTIPSDPM_GEN_READEN

           assign sop_empty_n = ~sop_empty;

        `endif

        `ifdef FFMEM_EXTERNAL
        r_fifo_23e
        `else
        r_fifo_23
        `endif
                  #(.FF_WIDTH(56),
                    .ADDR_WIDTH(ST_ADDR_WIDTH),
                    .DEPTH(ST_DEPTH),
                    .AF_THRESHOLD(3))

                U_SOPFF (
                  .reset_wclk(reset_ff_tx_clk),
                  .reset_rclk(reset_txclk),
                  .sw_reset_wclk(sw_reset_wclk),
                  .sw_reset_rclk(sw_reset_rclk),
                  .wclk(ff_tx_clk),
                  .wren(sop_wren_d),
                  .din(ff_tx_preamble_d),
                  .rclk(txclk),
                  .rden(sop_rden),
                `ifdef MTIPSDPM_GEN_READEN
                  .mem_rden(sop_empty_n),   // immediately produce output as long as non-empty
                `endif
                  .dout(sop_dout),
                  .afull(),
                `ifdef MTIPSDPM_GEN_READEN
                  .empty(sop_empty)
                `else
                  .empty()
                `endif
                `ifdef FFMEM_EXTERNAL
                  ,
                  .dout_err(),
                  .m_raddr(txp_raddr),
                  .m_rden( txp_rden ),
                  .m_rdata(txp_rdata),
                  .m_recc( 1'b 0 ),
                  .m_wren( txp_wren ),
                  .m_waddr(txp_waddr),
                  .m_wdata(txp_wdata) 
                `endif
                  );

        assign tx_preamble_int = sop_dout;

        // create read pulse after SOP was processed by MAC
        assign sop_rden = tx_rden_int_sop & txclk_ena;   // see tx_sop_int: must mask the very first before sop

`endif


// ----------
// ASSERTIONS
// ----------

// synthesis translate_off
// synopsys translate_off
`ifdef MTIPASSERTIONS   // enable assertions

always @(posedge reset_txclk or posedge txclk)
begin : assertions

reg asrt_mac_rden_d2;
reg asrt_underflow;

        if (reset_txclk == 1'b 1)
        begin
                asrt_mac_rden_d2 <= 1'b 0;
                asrt_underflow   <= 1'b 0;
        end
        else
        begin
                //
                // for MAC read edge detection
                //
                if (txclk_ena == 1'b 1)
                begin
                        asrt_mac_rden_d2 <= mac_rden_d;
                end

                //
                // detect FIFO underflow and remember until MAC restarts reading.
                //
                if (sw_reset_rclk == 1'b 1)
                begin
                        asrt_underflow <= 1'b 1;        // mask also for first after sync reset in case it was in middle of frame
                end
                else if (txclk_ena == 1'b 1)
                begin
                        if( ((dff_aempty==1'b 1 & stat_empty==1'b 1) | (tx_empty==1'b 1 & stat_empty==1'b 1)) & mac_rden_d==1'b 1 & tx_rden_int==1'b 1)
                        begin
                                asrt_underflow <= 1'b 1;
                        end
                        else if( ff_data_eop==1'b 1 )
                        begin
                                asrt_underflow <= 1'b 0;
                        end
                end

                //
                // ensure begin of frame always produces an SOP to the MAC
                // on underflow that must be masked as the MAC will stop read and restart as soon as non almost empty hence has no SOP.
                //
                if( asrt_underflow==1'b 0 && asrt_mac_rden_d2==0 && mac_rden_d==1 )
                begin
                        if( tx_sop_int!==1'b 1 )
                        begin
                                $display("%t ASSERT ***[tx_ff64v2id:%m]: ERROR: missing SOP to MAC from FIFO. FIFO read is corrupted", $time);
                                `ifdef MTIPASSERTIONS_STOP_ON_ERRORS
                                $stop(1);
                                `endif
                        end
                end
        end
end

    `ifdef USER_PREAMBLE_ENA

        always @(posedge reset_txclk or posedge txclk)
        begin : preamble_assertions

        reg asrt_sop_rden_d;

                if (reset_txclk == 1'b 1)
                begin
                        asrt_sop_rden_d <= 1'b 0;
                end
                else
                begin
                        // verify that the SOP (preamble) FIFO is never read within 2 consecutive cycles.
                        asrt_sop_rden_d <= sop_rden;

                        if( asrt_sop_rden_d==1'b 1 )
                        begin
                                if( !(sop_rden==1'b 0) )
                                begin
                                        $display("%t ASSERT ***[tx_ff64v2id:%m] Preamble FIFO read occured twice. Expect only one read per frame.", $time);
                                        `ifdef MTIPASSERTIONS_STOP_ON_ERRORS
                                        $stop(1);       // this is fatal
                                        `endif
                                end
                        end
                end
        end

    `endif

`endif
// synopsys translate_on
// synthesis translate_on

endmodule // module tx_ff64v2id
